﻿using System;
using System.ComponentModel;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;

namespace HML
{
    /// <summary>
    /// 主题管理器
    /// </summary>
    public static class SkinManager
    {
        #region 属性

        private static Dictionary<string, XmlDocument> allSkins = new Dictionary<string, XmlDocument>();
        /// <summary>
        /// 已经注册的主题集合
        /// </summary>
        internal static Dictionary<string, XmlDocument> AllSkins
        {
            get { return SkinManager.allSkins; }
        }

        private static string applySkinName = Skins.Dark.ToString();
        /// <summary>
        /// 当前正在使用主题
        /// </summary>
        public static string ApplySkinName
        {
            get { return SkinManager.applySkinName; }
        }

        private static List<SkinController> styleControllers = null;
        /// <summary>
        /// 主题控制器集合
        /// </summary>
        internal static List<SkinController> StyleControllers
        {
            get
            {
                if (SkinManager.styleControllers == null)
                    SkinManager.styleControllers = new List<SkinController>();
                return SkinManager.styleControllers;
            }
        }

        #endregion

        static SkinManager()
        {
            if (!SkinManager.AllSkins.ContainsKey(SkinManager.ApplySkinName))
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                using (Stream stream = assembly.GetManifestResourceStream("HML.Skin.Resources." + SkinManager.ApplySkinName + ".xml"))
                {
                    if (stream != null)
                    {
                        XmlDocument skinDocument = new XmlDocument();
                        skinDocument.Load(stream);
                        SkinManager.AllSkins.Add(SkinManager.ApplySkinName, skinDocument);
                    }
                }
            }
        }

        #region 公开方法

        /// <summary>
        /// 注册控件库自带所有主题到主题集合中
        /// </summary>
        public static void Register()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string[] skinNames = Enum.GetNames(typeof(Skins));
            for (int i = 0; i < skinNames.Length; i++)
            {
                if (!SkinManager.AllSkins.ContainsKey(skinNames[i]))
                {
                    using (Stream stream = assembly.GetManifestResourceStream("HML.Skin.Resources." + skinNames[i] + ".xml"))
                    {
                        XmlDocument skinDocument = new XmlDocument();
                        skinDocument.Load(stream);
                        SkinManager.AllSkins.Add(skinNames[i], skinDocument);
                    }
                }
            }
        }

        /// <summary>
        /// 注册控件库自带指定主题到主题集合中
        /// </summary>
        /// <param name="skin">自带指定主题</param>
        public static void Register(Skins skin)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string skinName = skin.ToString();
            if (!SkinManager.AllSkins.ContainsKey(skinName))
            {
                using (Stream stream = assembly.GetManifestResourceStream("HML.Skin.Resources." + skinName + ".xml"))
                {
                    XmlDocument skinDocument = new XmlDocument();
                    skinDocument.Load(stream);
                    SkinManager.AllSkins.Add(skinName, skinDocument);
                }
            }
        }

        /// <summary>
        /// 注册外部主题到主题集合中
        /// </summary>
        /// <param name="skinPath">外部主题xml文件物理路径</param>
        public static void Register(string skinPath)
        {
            if (!File.Exists(skinPath))
            {
                throw new Exception(skinPath + " 主题xml文件不存在");
            }

            string skinName = Path.GetFileNameWithoutExtension(skinPath).Trim();
            if (!SkinManager.AllSkins.ContainsKey(skinName))
            {
                XmlDocument skinDocument = new XmlDocument();
                skinDocument.Load(skinPath);
                SkinManager.AllSkins.Add(skinName, skinDocument);
            }
        }

        /// <summary>
        /// 注册外部主题到主题集合中
        /// </summary>
        /// <param name="skinPaths">外部主题xml文件物理路径集合</param>
        public static void Register(string[] skinPaths)
        {
            if (skinPaths == null)
                return;

            for (int i = 0; i < skinPaths.Length; i++)
            {
                if (!File.Exists(skinPaths[i]))
                {
                    throw new Exception(skinPaths[i] + " 主题xml文件不存在");
                }

                string skinName = Path.GetFileNameWithoutExtension(skinPaths[i]).Trim();
                if (!SkinManager.AllSkins.ContainsKey(skinName))
                {
                    XmlDocument skinDocument = new XmlDocument();
                    skinDocument.Load(skinPaths[i]);
                    SkinManager.AllSkins.Add(skinName, skinDocument);
                }
            }
        }

        /// <summary>
        /// 注册外部主题到主题集合中
        /// </summary>
        /// <param name="skinName">主题名称</param>
        /// <param name="skinDocument">外部主题xml文档</param>
        public static void Register(string skinName, XmlDocument skinDocument)
        {
            skinName = skinName.Trim();
            if (!SkinManager.AllSkins.ContainsKey(skinName))
            {
                SkinManager.AllSkins.Add(skinName, skinDocument);
            }
        }

        /// <summary>
        /// 已注册的主题集合是否包含该主题
        /// </summary>
        /// <param name="skinName">主题名称</param>
        /// <returns></returns>
        public static bool IsRegister(string skinName)
        {
            return SkinManager.AllSkins.ContainsKey(skinName.Trim());
        }

        /// <summary>
        /// 获取已注册的主题名称
        /// </summary>
        public static string[] GetRegisterSkinNames()
        {
            string[] result = new string[SkinManager.AllSkins.Count];
            SkinManager.AllSkins.Keys.CopyTo(result, 0);
            return result;
        }

        /// <summary>
        /// 应用指定主题
        /// </summary>
        /// <param name="skinName">主题名称</param>
        public static void Apply(string skinName)
        {
            skinName = skinName.Trim();

            if (!SkinManager.AllSkins.ContainsKey(skinName))
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                using (Stream stream = assembly.GetManifestResourceStream("HML.Skin.Resources." + skinName + ".xml"))
                {
                    if (stream != null)
                    {
                        XmlDocument skinDocument = new XmlDocument();
                        skinDocument.Load(stream);
                        SkinManager.AllSkins.Add(skinName, skinDocument);
                    }
                }
            }

            if (!SkinManager.AllSkins.ContainsKey(skinName))
            {
                throw new Exception(skinName + " 应用失败，主题不存在");
            }

            SkinManager.applySkinName = skinName;

            for (int i = 0; i < SkinManager.StyleControllers.Count; i++)
            {
                SkinManager.StyleControllers[i].OnSkinChanged();
            }
        }

        /// <summary>
        /// 同步主题文件配置到主题对象的所有主题属性上
        /// </summary>
        /// <param name="skinObject">主题对象</param>
        public static void SyncSkinValueToProperty(ISkinObject skinObject)
        {
            PropertyInfo property = skinObject.GetType().GetProperty("StyleAppearance", BindingFlags.Public | BindingFlags.Instance);
            string skinObjectXPath = skinObject.SkinObjectXmlMetadata.GetSkinObjectXPath() + "/" + skinObject.SkinStyle.ToString();
            SkinManager.RecurrenceSyncSkinValue(SkinManager.AllSkins[SkinManager.ApplySkinName], skinObject, skinObjectXPath, property);
        }

        /// <summary>
        /// 获取指定主题对象的主题启用状态
        /// </summary>
        /// <param name="skinObject">指定主题对象</param>
        /// <returns></returns>
        public static bool GetSkinObjectSkinState(ISkinObject skinObject)
        {
            if (skinObject.SkinEnabled == SkinEnabledState.True)
                return true;

            if (skinObject.SkinEnabled == SkinEnabledState.Auto && skinObject.SkinController != null && skinObject.SkinController.SkinEnabled == SkinEnabledState.True)
                return true;

            return false;
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 递归同步主题文件配置到主题对象的主题属性上
        /// </summary>
        /// <param name="skinDocument">要同步的主题文件配置</param>
        /// <param name="propertyOwnerObject">属性所在对象</param>
        /// <param name="propertyOwnerXPath">属性所在对象在主题配置文件的XPath路径</param>
        /// <param name="property">要同步的属性元数据</param>
        private static void RecurrenceSyncSkinValue(XmlDocument skinDocument, object propertyOwnerObject, string propertyOwnerXPath, PropertyInfo property)
        {
            string propertyXPath = propertyOwnerXPath + "/" + property.Name;

            if (property.PropertyType.IsSubclassOf(typeof(AppearanceObjectBase)))
            {
                PropertyInfo[] childrenPropertys = property.PropertyType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo childrenProperty in childrenPropertys)
                {
                    if (childrenProperty.IsDefined(typeof(SkinPropertyAttribute), true))
                    {
                        SkinPropertyAttribute skinPropertyAttribute = (SkinPropertyAttribute)childrenProperty.GetCustomAttributes(typeof(SkinPropertyAttribute), true)[0];
                        if (skinPropertyAttribute.Enabled)
                        {
                            SkinManager.RecurrenceSyncSkinValue(skinDocument, property.GetValue(propertyOwnerObject, null), propertyXPath, childrenProperty);
                        }
                    }
                }
            }
            else
            {
                XmlNode xmlNode = skinDocument.SelectSingleNode(propertyXPath);
                if (xmlNode != null)
                {
                    if (typeof(Color).FullName == xmlNode.Attributes["ValueType"].Value)
                    {
                        Color color_value = Color.Empty;

                        string xml_value = xmlNode.Attributes["Value"].Value.Trim();
                        string[] argb = xml_value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                        if (xml_value == "")
                        {

                        }
                        else if (argb.Length == 3)
                        {
                            color_value = Color.FromArgb(byte.Parse(argb[0]), byte.Parse(argb[1]), byte.Parse(argb[2]));
                        }
                        else if (argb.Length == 4)
                        {
                            int a = byte.Parse(argb[0]);
                            int r = byte.Parse(argb[1]);
                            int g = byte.Parse(argb[2]);
                            int b = byte.Parse(argb[3]);

                            color_value = (a == 0 && r == 0 && g == 0 && b == 0) ? Color.Empty : Color.FromArgb(a, r, g, b);
                        }
                        else if (xml_value.StartsWith("#"))
                        {
                            color_value = ColorTranslator.FromHtml(xml_value);
                        }
                        else
                        {
                            color_value = Color.FromName(xml_value);
                        }

                        FieldInfo propertyField = propertyOwnerObject.GetType().GetField("skin" + property.Name, BindingFlags.NonPublic | BindingFlags.Instance);
                        propertyField.SetValue(propertyOwnerObject, color_value);
                    }
                }
            }
        }

        #endregion
    }

}
